#include "hw_memmap.h"
#include "hw_types.h"
#include "debug.h"
#include "gpio.h"
#include "uart.h"
#include "pwm.h"
#include "adc.h"
#include "menu.h"

#define RISE_FALL_MAX 4096
#define MAX_PERIOD_DOWN 65537
#define MAX_PERIOD_UP_DOWN 131072

//
//	Detailed configuration for the selected PWM
//
void ADC_config(struct ADC_parameters * ADC, char c);
//
//	Detailed configuration for the selected ADC
//
void PWM_nastroiki(struct PWM_parameters *PWM);


 
//*****************************************************************************
//	Function for setting up the parameters of the LED on the kit.
//By sending the user messages and possible choices this function can 
//acquire the needed parameters to set the drive strength and type for the LED.
//Where the strength can be 2,4,8 mA, and the type can be standard with/without
//weak pull up/down or open-drain with/without weak pull up/down.
//*****************************************************************************
struct LB led(struct LB Led_Button){
	//
	//Check if the led is already configured
	//
	if(Led_Button.led_and_button[0] == 0){	
		UARTSend((unsigned char *)"\n\rEnter choice for LED drive strength:\n\r"
								"1. 2mA drive strength\n\r"
								"2. 4mA drive strength\n\r"
								"3. 8mA drive strength\n\r"
								"4. 8mA drive with slew rate control\n\r",146);
		switch(ChoiseNum(4))
		{
		case 1: Led_Button.led_curent = GPIO_STRENGTH_2MA;  break;
		case 2: Led_Button.led_curent = GPIO_STRENGTH_4MA ; break;
		case 3: Led_Button.led_curent = GPIO_STRENGTH_8MA ; break;
		case 4: Led_Button.led_curent = GPIO_STRENGTH_8MA_SC; break;
		default: break;
		} 
	    UARTSend((unsigned char *)"\n\rEnter choice:\n\r"
									"1. Push-pull\n\r"
									"2. Push-pull with weak pull-up\n\r"
									"3. Push-pull with weak pull-down\n\r"
									"4. Open-drain\n\r"
									"5. Open-drain with weak pull-up\n\r"
									"6. Open-drain with weak pull-down\n\r"
									"7. Analog comparator\n\r",202);
		switch(ChoiseNum(7))
		{
		case 1: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_STD; break;
		case 2: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_STD_WPU; break;
		case 3: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_STD_WPD; break;
		case 4: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_OD; break;
		case 5: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_OD_WPU; break;
		case 6: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_OD_WPD; break;
		case 7: Led_Button.led_PUSH_PULL = GPIO_PIN_TYPE_ANALOG; break;
		default: break;
		}
		Led_Button.led_and_button[0] = 1;
	 	UARTSend((unsigned char *)"\n\rLed is configured successfully.\n\r" ,35);
		return Led_Button;
	 }else{
	 	UARTSend((unsigned char *)"\n\rLed is already configured!\n\r" ,30);
		return Led_Button;
	 }



}

//*****************************************************************************
//	Function for setting up the parameters of the button on the kit.
//By sending the user messages and possible choices this function 
//can acquire the needed parameters to set up the interrupt type
//for the button. For example the interrupt can be set for rising 
//edges or low level and etc.
//*****************************************************************************

struct LB button(struct LB Led_Button){
	 // 
	 //Check if the button is already configured
     //
	 if(Led_Button.led_and_button[1] == 0){
	 	
	    UARTSend((unsigned char *)"\n\rEnter a choice for BUTTON interrupt:\n\r"
									"1.Falling edge detect\n\r"
									"2.Rising edge detect\n\r"
									"3.Both edges detect\n\r"
									"4.Low level detect\n\r"
									"5.High level detect\n\r"
									"6.Without interrupts \n\r",170);
		switch(ChoiseNum(6))
		{
			case 1: Led_Button.int_det_mech = GPIO_FALLING_EDGE;  break;
			case 2: Led_Button.int_det_mech = GPIO_RISING_EDGE;  break;
			case 3: Led_Button.int_det_mech = GPIO_BOTH_EDGES;  break;
			case 4: Led_Button.int_det_mech = GPIO_LOW_LEVEL;  break;
			case 5: Led_Button.int_det_mech = GPIO_HIGH_LEVEL;  break;
			case 6: Led_Button.int_det_mech = 0;  break;
		}
		Led_Button.led_and_button[1] = 1;
	 	UARTSend((unsigned char *)"\n\rButton is configured successfully.\n\r" ,38); 
		return Led_Button;
	 }else{
	 	UARTSend((unsigned char *)"\n\rButton is already configured!\n\r" ,33);
		return Led_Button;
	 }

}

//*****************************************************************************
//	Initial settings for the PWM
//First checking for configured PWMs, if all are configured a message is send.
//Setting the common parameters for all PWMs. Sending message via the UART 
//for choosing PWM and entering PWM_nastroiki
//*****************************************************************************

void PwmSet(struct PWM_parameters * PWM){
	//
    //Check if PWM module is configured
	//
	if((PWM[0].check | PWM[1].check | PWM[2].check) == 0){
		UARTSend((unsigned char *)"You have chosen PWM module!\n\r",29);
		PWM[0].genNum=PWM_GEN_0;
		PWM[1].genNum=PWM_GEN_1;
		PWM[2].genNum=PWM_GEN_2;
		PWM[0].outputbitA=PWM_OUT_0_BIT;
		PWM[0].outputbitB=PWM_OUT_1_BIT;
		PWM[1].outputbitA=PWM_OUT_2_BIT;
		PWM[1].outputbitB=PWM_OUT_3_BIT;
		PWM[2].outputbitA=PWM_OUT_4_BIT;
		PWM[2].outputbitB=PWM_OUT_5_BIT;
		PWM[0].ulGenFault=PWM_INT_GEN_0;
		PWM[1].ulGenFault=PWM_INT_GEN_1;
		PWM[2].ulGenFault=PWM_INT_GEN_2;
		while(1){
			UARTSend((unsigned char *)"Choose a PWM Generator from 1 to 3:\n\r",37); 
			PWM_nastroiki(PWM + (ChoiseNum(3)-1));
		   	UARTSend((unsigned char *)"Exit: 1.YES 2.NO\n\r",18);
			if(ChoiseNum(2) == 2 )
				continue;
			else break;
		}
	}else{
	UARTSend((unsigned char *)"\n\rPWM module is already configured!\n\r",36);
	}	


	
			
}

//*****************************************************************************
//	Detailed configuration for the selected PWM
//Check for configured PWM, if not by sending the user messages and possible 
//choices this function can acquire the needed parameters to set up the PWM 
//like working mode (down/up-down), with or without synchronization, debug 
//mode working while debug or to stop when debug. Also the width of the 
//falling(rising) edge, the period and the pulse width, the interrupt 
//type or the trigger events, etc
//*****************************************************************************

void PWM_nastroiki(struct PWM_parameters * PWM){
	short choise;
	unsigned long mode = 0,rej = 0,dbg = 0;
	//
	//Check if the generator is already configured
	//
	if(PWM->check==0)
	{
			UARTSend((unsigned char *)"\n\rChoose mode:\n\r"
			                               "1.COUNT DOWN\n\r"
										   "2.COUNT UP/DOWN\n\r",46);
			choise=ChoiseNum(2);
			switch(choise){
				case 1:mode=PWM_GEN_MODE_DOWN;break;
				case 2:mode= PWM_GEN_MODE_UP_DOWN;break;
				default: break;   
			}
			UARTSend((unsigned char *)"\n\rChoose configuration:\n\r"
			                               "1.Synch\n\r"
										   "2.No-synch\n\r",46);
			choise=ChoiseNum(2);
			switch(choise){
				case 1:rej=PWM_GEN_MODE_SYNC;break;
				case 2:rej= PWM_GEN_MODE_NO_SYNC;break;
				default: break;   
			}

		   UARTSend((unsigned char *)"\n\rDebug mode:\n\r"
		                               "1.Stop\n\r"
									   "2.run\n\r",30);

			choise=ChoiseNum(2);
			switch(choise){
				case 1:dbg=PWM_GEN_MODE_DBG_RUN;break;
				case 2:dbg= PWM_GEN_MODE_DBG_STOP;break;
				default: break;   
			}
			PWM->config = mode|rej|dbg;
		    UARTSend((unsigned char *)"\n\rDead band:\n\r"
		                               "1.YES\n\r"
									   "2.NO\n\r",27);
			choise=ChoiseNum(2);
			PWM->deadBand = 0;
		 	if(choise==1)
			{
				PWM->deadBand=1;
				UARTSend((unsigned char *)"\n\rSpecify the width of delay from the rising edge < 4096:\n\r" ,59);
				PWM->rise=Choice(RISE_FALL_MAX);
				UARTSend((unsigned char *)"\n\rSpecify the width of delay from the falling edge < 4096:\n\r",60);
				PWM->fall=Choice(RISE_FALL_MAX);                           
			}
			UARTSend((unsigned char *)"\n\rSet the period of the PWM generator:\n\r" ,40);
			if(mode != PWM_GEN_MODE_DOWN){
				UARTSend((unsigned char *)"\n\rIn up/down count mode,the period must be < 131072!\n\r" ,54);
				PWM->period=Choice(MAX_PERIOD_UP_DOWN);
			} else{
				UARTSend((unsigned char *)"\n\rIn down count mode,the period must be <= 65536!\n\r" ,51);
				PWM->period=Choice(MAX_PERIOD_DOWN);
			}

		    UARTSend((unsigned char *)"\n\rSets the pulse width for the PWM output one.\n\r" 
										"It must be <= period!\n\r",71);
			PWM->pulsewidth[0]=Choice((PWM->period)+1);
			if(PWM->deadBand==0){
				UARTSend((unsigned char *)"\n\rSets the pulse width for the PWM output two.\n\r" 
											"It must be <= period!\n\r",71);
				PWM->pulsewidth[1]=Choice((PWM->period)+1);
			}
            UARTSend((unsigned char *)"\n\rEnables interrupts?\n\r"
		                               "1.Yes\n\r"
									   "2.No\n\r",36);
			PWM->interrupt=0;						  
			if(ChoiseNum(2) == 1){
				PWM->interrupt=1;	
				UARTSend((unsigned char *)"\n\rChoose interrupt or trigger event:\n\r"
										  "1.Interrupts\n\r"
										  "2.Trigger\n\r",63);
				choise=ChoiseNum(2);
				if(choise==1&&mode==PWM_GEN_MODE_UP_DOWN)
				{
					 UARTSend((unsigned char *)"\n\rChoose interrupt event:\n\r"
											   "1.LOAD\n\r"
											   "2.ZERO\n\r"
											   "3.AU\n\r"
											   "4.BU\n\r"
											   "5.AD\n\r"
											   "6.BD\n\r",67);
					switch(ChoiseNum(6)){
						case 1:PWM->ulIntTrig=PWM_INT_CNT_LOAD;break;
						case 2:PWM->ulIntTrig=PWM_INT_CNT_ZERO;break;
						case 3:PWM->ulIntTrig=PWM_INT_CNT_AU;break;
						case 4:PWM->ulIntTrig=PWM_INT_CNT_BU;break;
						case 5:PWM->ulIntTrig=PWM_INT_CNT_AD;break;
						case 6:PWM->ulIntTrig=PWM_INT_CNT_BD;break;
						default: break;   
					}
				}
				if((choise==1)&&(mode==PWM_GEN_MODE_DOWN)){
					UARTSend((unsigned char *)"\n\rChoose interrupt event:\n\r"
											   "1.LOAD\n\r"
											   "2.ZERO\n\r"
											   "3.AD\n\r"
											   "4.BD\n\r",55);
					switch(ChoiseNum(4)){
						case 1:PWM->ulIntTrig=PWM_INT_CNT_LOAD;break;
						case 2:PWM->ulIntTrig=PWM_INT_CNT_ZERO;break;
						case 3:PWM->ulIntTrig=PWM_INT_CNT_AD;break;
						case 4:PWM->ulIntTrig=PWM_INT_CNT_BD;break;
						default: break;
					}
				}
				if(choise==2&&mode==PWM_GEN_MODE_UP_DOWN)
				{
					 UARTSend((unsigned char *)"\n\rChoose trigegr event:\n\r"
											   "1.LOAD\n\r"
											   "2.ZERO\n\r"
											   "3.AU\n\r"
											   "4.BU\n\r"
											   "5.AD\n\r"
											   "6.BD\n\r",65);
					switch(ChoiseNum(6)){
						case 1:PWM->ulIntTrig=PWM_TR_CNT_LOAD;break;
						case 2:PWM->ulIntTrig=PWM_TR_CNT_ZERO;break;
						case 3:PWM->ulIntTrig=PWM_TR_CNT_AU;break;
						case 4:PWM->ulIntTrig=PWM_TR_CNT_BU;break;
						case 5:PWM->ulIntTrig=PWM_TR_CNT_AD;break;
						case 6:PWM->ulIntTrig=PWM_TR_CNT_BD;break;
						default: break;   
					}
				}
				if((choise==2)&&(mode==PWM_GEN_MODE_DOWN)){
					 UARTSend((unsigned char *)"\n\rChoose trigegr event:\n\r"
											   "1.LOAD\n\r"
											   "2.ZERO\n\r"
											   "3.AD\n\r"
											   "4.BD\n\r",53);
					switch(ChoiseNum(4)){
						case 1:PWM->ulIntTrig=PWM_TR_CNT_LOAD;break;
						case 2:PWM->ulIntTrig=PWM_TR_CNT_ZERO;break;
						case 3:PWM->ulIntTrig=PWM_TR_CNT_AD;break;
						case 4:PWM->ulIntTrig=PWM_TR_CNT_BD;break;
						default: break;
					}
				}
			}
		    UARTSend((unsigned char *)"\n\rEnables a fault interrupts for a PWM module?\n\r"
	                                  "1.Yes\n\r"
					  	              "2.No\n\r",61);
			choise=ChoiseNum(2);
			PWM->faultinterrupt=0;
			if(choise==1)
			{
				PWM->faultinterrupt=1;
				UARTSend((unsigned char *)"\n\rFault condition for output one:\n\r"
										  "1.TRUE\n\r"
										  "2.FALSE\n\r",52);
				choise=ChoiseNum(2);
		   		switch(choise){
					case 1:PWM->output[0]=1;break;
					case 2:PWM->output[0]=0;break;
					default: break;
				}
				UARTSend((unsigned char *)"n\r\Fault condition for output two\n\r"
										  "1.TRUE\n\r"
										  "2.FALSE\n\r",52);
				choise=ChoiseNum(2);
				switch(choise){
					case 1:PWM->output[1]=1;break;
					case 2:PWM->output[1]=0;break;
					default: break;
				}
			}
			UARTSend((unsigned char *)"\n\rInversion for PWM outputs?\n\r"
	                                  "1.Yes\n\r"
								      "2.No\n\r",43);
			choise=ChoiseNum(2);
			if(choise==1)
			{
				UARTSend((unsigned char *)"\n\rInversion for PWM output one?\n\r"
										  "1.Yes\n\r"
										  "2.No\n\r",46);
				choise=ChoiseNum(2);
				switch(choise){
					case 1:PWM->invert[0]=1;break;
					case 2:PWM->invert[0]=0;break;
					default: break;
				}

				UARTSend((unsigned char *)"\n\rInversion for PWM output two?\n\r"
										  "1.Yes\n\r"
										  "2.No\n\r",46);
				choise=ChoiseNum(2);
				switch(choise){
					case 1:PWM->invert[1]=1;break;
					case 2:PWM->invert[1]=0;break;
					default: break;
				}
			}
		UARTSend((unsigned char *)"\n\rPWM is configured successfully.\n\r" ,35);
		PWM->check = 1;								  						   
	}
	else
	{
			UARTSend((unsigned char *)"\n\rPWM is already configured!\n\r" ,30);
	}
}
	 

//*****************************************************************************
//	Function used for choosing which ADC to start.
//First checking for configured ADCs, if all are configured a message is send 
//else sending message via the UART for choosing ADC and entering ADC_config.
//*****************************************************************************

void AdcSet(struct ADC_parameters * ADC){
	//
	//Check if ADC module is configured
	//
	if((ADC[0].check | ADC[1].check | ADC[2].check | ADC[3].check) == 0){
		UARTSend((unsigned char *)"You have chosen ADC module!\n\r",29);
		while(1){
			UARTSend((unsigned char *)"Choose a ADC from 1 to 4:\n\r",27);
			ADC_config(ADC , (ChoiseNum(4)-1));
		   	UARTSend((unsigned char *)"Exit: 1.YES 2.NO\n\r",18);
			if(ChoiseNum(2) == 2 )
				continue;
			else 
				break;
		}
	}
	else
	{
		UARTSend((unsigned char *)"\n\rADC module is already configured!\n\r",36);
	}

}
//*****************************************************************************
//	Detailed configuration for the selected ADC
//Check for configured ADC, if not by sending the user messages and possible
//choices this function can acquire the needed parameters to set up the ADC 
//like the type of trigger, priority, software over sampling and over 
//sampling factor corresponding to the selected ADC, the order of the 
//samples to be taken channel to scan and/or the temperature sensor, 
//enabled interrupts, etc.
//*****************************************************************************
void ADC_config(struct ADC_parameters * ADC, char c){
	unsigned long choise,k;				   
	//
    //Check if the ADC is already configured
	//
	ADC+=c;

	if(ADC->check==0)
	{			
		UARTSend((unsigned char *)"\n\rChoose Trigger and press Enter:\n\r"
				                               "1.Processor\n\r"
											   "2.Comparator 1\n\r"
											   "3.Comparator 2\n\r"
											   "4.Comparator 3\n\r"
											   "5.External Port B4 pin\n\r"
											   "6.Timer\n\r"
											   "7.PWM1\n\r"		 
											   "8.PWM2\n\r"
											   "9.PWM3\n\r"
											   "10.Always\n\r",165); 
	
		switch (Choice(11))
		{
			case 1: ADC->ulTrigger=ADC_TRIGGER_PROCESSOR; break;
			case 2: ADC->ulTrigger=ADC_TRIGGER_COMP0; break;
			case 3: ADC->ulTrigger=ADC_TRIGGER_COMP1; break;
			case 4: ADC->ulTrigger=ADC_TRIGGER_COMP2; break;
			case 5:	ADC->ulTrigger=ADC_TRIGGER_EXTERNAL; break;
			case 6:	ADC->ulTrigger=ADC_TRIGGER_TIMER; break;
			case 7:	ADC->ulTrigger=ADC_TRIGGER_PWM0; break;
			case 8:	ADC->ulTrigger=ADC_TRIGGER_PWM1; break;
			case 9:	ADC->ulTrigger=ADC_TRIGGER_PWM2; break;
			case 10: ADC->ulTrigger=ADC_TRIGGER_ALWAYS; break;
			default: break;
		}		  
	
		UARTSend((unsigned char *)"\n\rChoose Priority:\n\r"
				                               "1.1 - The greatest;\n\r"
											   "2.2\n\r"
											   "3.3\n\r"
											   "4.4 - Least\n\r",65); 
		ADC->ulPriority = ChoiseNum(4)-1;
		ADC->ulStep = 0;
		
	
		UARTSend((unsigned char *)"\n\rOversample:\n\r"
				                               "1.Yes\n\r"
											   "2.No\n\r",28);
		choise = ChoiseNum(2);
		if (choise==1) 
		{
			if (c==3)
			{
				UARTSend((unsigned char *)"\n\rOversample Factor: 2x\n\r",25);
				ADC->ulOversampleFactor=2;
			}
			else
			{
				UARTSend((unsigned char *)"\n\rOversample Factor:\n\r"
					                      "1.2x\n\r",29);
				if (c==1 || c==2)
				{
					UARTSend((unsigned char *)"2.4x\n\r",6);
					choise=ChoiseNum(2);
				}
				if (c==0)
				{
					UARTSend((unsigned char *)"2.4x\n\r"
											  "3.8x\n\r",12);
					choise=ChoiseNum(3);
				}
				ADC->ulOversampleFactor=1<<choise;
			}
			
		}
	
	   	UARTSend((unsigned char *)"\n\rChoose order of accepting(1-8 for SS0; 1-4 fot SS1 and SS2):\n\r",64);
	
		if (c==0)
		{
			ADC->ulStep = ChoiseNum(8)-1;
		}
		if (c==1 || c==2)
		{
			ADC->ulStep = ChoiseNum(4)-1;
		}
	
		UARTSend((unsigned char *)"\n\rChoose configuration of the step:\n\r"
				                  "1.Single-ended\n\r"
								  "2.Differential\n\r",69);
		choise = ChoiseNum(2);
		switch (choise)
		{
			case 1: ADC->ulStepConfig=0; break;
			case 2:	ADC->ulStepConfig=ADC_CTL_D; break;
			default: return;
		}
	
		if (choise==1) 
		{
			UARTSend((unsigned char *)"\n\rDo you want to select the temprerature sensor:\n\r"
														"1.Yes\n\r"
														"2.No\n\r",63);
			if (ChoiseNum(2) == 1) ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_TS;
		}

		k=1;
		UARTSend((unsigned char *)"\n\rChoose channel to scan:\n\r"
									"1.CH0\n\r",34);

		if (c==1 || c==2)
		{
			UARTSend((unsigned char *)"2.CH1\n\r"
									  "3.CH2\n\r"
									  "4.CH3\n\r",21);
			k=4;
		}

		if (c==0)
		{
			UARTSend((unsigned char *)"2.CH1\n\r"
									  "3.CH2\n\r"
									  "4.CH3\n\r"
									  "5.CH4\n\r"
									  "6.CH5\n\r"
									  "7.CH6\n\r"
									  "8.CH7\n\r",49);
			k=8;
		} 
		switch (ChoiseNum(k))
		{
			case 1: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH0; break;
			case 2: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH1; break;
			case 3: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH2; break;
			case 4: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH3; break;
			case 5: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH4; break;
			case 6: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH5; break;
			case 7: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH6; break;
			case 8: ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_CH7; break;
			default: break;
		}
		
		UARTSend((unsigned char *)"\n\rInterrupt enable:\n\r"
												"1.Yes\n\r"
												"2.No\n\r",34);
		if (ChoiseNum(2) == 1)
			ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_IE;
	
		UARTSend((unsigned char *)"\n\rLast Step:\n\r"
												"1.Yes\n\r"
												"2.No\n\r",27);
		if (ChoiseNum(2) == 1)
			ADC->ulStepConfig=ADC->ulStepConfig | ADC_CTL_END;
	
		UARTSend((unsigned char *)"\n\rADC is configured successfully.\n\r" ,35);		
		ADC->check=1;								  						   
	}
	else
	{
			UARTSend((unsigned char *)"\n\rADC is already configured!\n\r" ,30);
	}

}
